Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ

Node / meshtastic / Meshtastic-Android / files / .github / workflows / scheduled-updates.yml

Displaying Raw β€’ Download

.github/workflows/scheduled-updates.yml chore/develocity-tuning (359761e6) Text, 17.79 KB

Tff7b72nameTb4b4b4: Ta5d6ffScheduledTa5d6ff Ta5d6ffUpdatesTa5d6ff Ta5d6ff(Firmware,Ta5d6ff Ta5d6ffHardware,Ta5d6ff Ta5d6ffTranslations)

Tff7b72onTb4b4b4:
Tff7b72scheduleTb4b4b4:
T8b949e# Hourly. This job is cheap (curl + Crowdin + PR, no build) β€” the slow
T8b949e# emulator/graph work lives in scheduled-baseline.yml on a daily cron.
Tb4b4b4- Tff7b72cronTb4b4b4: Ta5d6ff'Ta5d6ff0Te6edf3 Ta5d6ff*Te6edf3 Ta5d6ff*Te6edf3 Ta5d6ff*Te6edf3 Ta5d6ff*Ta5d6ff'
Tff7b72workflow_dispatchTb4b4b4: T8b949e# Allow manual triggering

T8b949e# Hourly cron + manual dispatch must never stack: overlapping runs race on the
T8b949e# scheduled-updates branch force-push. Later runs queue (at most one pending).
Tff7b72concurrencyTb4b4b4:
Tff7b72groupTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.workflowTa5d6ff Ta5d6ff}}
Tff7b72cancel-in-progressTb4b4b4: Ta5d6fffalse

Tff7b72jobsTb4b4b4:
Tff7b72update_assetsTb4b4b4:
Tff7b72runs-onTb4b4b4: Ta5d6ffubuntu-24.04
Tff7b72timeout-minutesTb4b4b4: Ta5d6ff30
Tff7b72ifTb4b4b4: Ta5d6ffgithub.repositoryTa5d6ff Ta5d6ff==Ta5d6ff Ta5d6ff'meshtastic/Meshtastic-Android'
Tff7b72permissionsTb4b4b4:
Tff7b72contentsTb4b4b4: Ta5d6ffwrite T8b949e# To commit files and push branches
Tff7b72pull-requestsTb4b4b4: Ta5d6ffwrite T8b949e# To create pull requests

Tff7b72stepsTb4b4b4:
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCheckoutTa5d6ff Ta5d6ffrepository
Tff7b72usesTb4b4b4: Ta5d6ffactions/checkout@v7.0.1
Tff7b72withTb4b4b4:
Tff7b72tokenTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsecrets.CROWDIN_GITHUB_TOKENTa5d6ff Ta5d6ff}}

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffUpdateTa5d6ff Ta5d6fffirmwareTa5d6ff Ta5d6ffreleasesTa5d6ff Ta5d6fflist
Tff7b72idTb4b4b4: Ta5d6fffirmware
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72firmware_file_path="androidApp/src/main/assets/firmware_releases.json"
Tff7b72temp_firmware_file="/tmp/new_firmware_releases.json"

Tff7b72echo "Fetching latest firmware releases..."
Tff7b72http_code=$(curl -s -o "$temp_firmware_file" -w '%{http_code}' https://api.meshtastic.org/github/firmware/list || true)
Tff7b72http_code="${http_code:-0}"

Tff7b72if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
Tff7b72echo "::warning::Firmware API returned HTTP $http_code. Skipping firmware update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=HTTP $http_code from firmware API" >> "$GITHUB_OUTPUT"
Tff7b72elif ! jq empty "$temp_firmware_file" 2>/dev/null; then
Tff7b72echo "::warning::Firmware API returned invalid JSON data. Skipping firmware update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=Invalid JSON response from firmware API" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72# Drop `.pullRequests` before diffing and before writing: the API lists every open
Tff7b72# firmware PR, which turns over several times a day, and the app never reads the field
Tff7b72# (see NetworkFirmwareReleases). Keeping it churned an hourly PR carrying no real change.
Tff7b72if [ ! -f "$firmware_file_path" ] || ! jq --sort-keys 'del(.pullRequests)' "$temp_firmware_file" | diff -q - <(jq --sort-keys 'del(.pullRequests)' "$firmware_file_path"); then
Tff7b72echo "Changes detected in firmware list or local file missing. Updating $firmware_file_path."
Tff7b72jq 'del(.pullRequests)' "$temp_firmware_file" > "$firmware_file_path"
Tff7b72echo "status=updated" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72echo "No changes detected in firmware list."
Tff7b72echo "status=unchanged" >> "$GITHUB_OUTPUT"
Tff7b72fi
Tff7b72fi

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffUpdateTa5d6ff Ta5d6ffhardwareTa5d6ff Ta5d6fflist
Tff7b72idTb4b4b4: Ta5d6ffhardware
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72hardware_file_path="androidApp/src/main/assets/device_hardware.json"
Tff7b72temp_hardware_file="/tmp/new_device_hardware.json"

Tff7b72echo "Fetching latest device hardware data..."
Tff7b72http_code=$(curl -s -o "$temp_hardware_file" -w '%{http_code}' https://api.meshtastic.org/resource/deviceHardware || true)
Tff7b72http_code="${http_code:-0}"

Tff7b72if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
Tff7b72echo "::warning::Hardware API returned HTTP $http_code. Skipping hardware update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=HTTP $http_code from hardware API" >> "$GITHUB_OUTPUT"
Tff7b72elif ! jq empty "$temp_hardware_file" 2>/dev/null; then
Tff7b72echo "::warning::Hardware API returned invalid JSON data. Skipping hardware update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=Invalid JSON response from hardware API" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72if [ ! -f "$hardware_file_path" ] || ! jq --sort-keys . "$temp_hardware_file" | diff -q - <(jq --sort-keys . "$hardware_file_path"); then
Tff7b72echo "Changes detected in hardware list or local file missing. Updating $hardware_file_path."
Tff7b72cp "$temp_hardware_file" "$hardware_file_path"
Tff7b72echo "status=updated" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72echo "No changes detected in hardware list."
Tff7b72echo "status=unchanged" >> "$GITHUB_OUTPUT"
Tff7b72fi
Tff7b72fi

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffUpdateTa5d6ff Ta5d6ffeventTa5d6ff Ta5d6fffirmwareTa5d6ff Ta5d6ffmetadata
Tff7b72idTb4b4b4: Ta5d6ffevent_firmware
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72event_firmware_file_path="androidApp/src/main/assets/event_firmware.json"
Tff7b72temp_event_firmware_file="/tmp/new_event_firmware.json"

Tff7b72echo "Fetching latest event firmware metadata..."
Tff7b72http_code=$(curl -s --max-time 90 -o "$temp_event_firmware_file" -w '%{http_code}' https://api.meshtastic.org/resource/eventFirmware || true)
Tff7b72http_code="${http_code:-0}"

Tff7b72if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
Tff7b72echo "::warning::Event firmware API returned HTTP $http_code. Skipping event firmware update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=HTTP $http_code from event firmware API" >> "$GITHUB_OUTPUT"
Tff7b72elif ! jq empty "$temp_event_firmware_file" 2>/dev/null; then
Tff7b72echo "::warning::Event firmware API returned invalid JSON data. Skipping event firmware update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=Invalid JSON response from event firmware API" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72if [ ! -f "$event_firmware_file_path" ] || ! jq --sort-keys . "$temp_event_firmware_file" | diff -q - <(jq --sort-keys . "$event_firmware_file_path"); then
Tff7b72echo "Changes detected in event firmware metadata or local file missing. Updating $event_firmware_file_path."
Tff7b72cp "$temp_event_firmware_file" "$event_firmware_file_path"
Tff7b72echo "status=updated" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72echo "No changes detected in event firmware metadata."
Tff7b72echo "status=unchanged" >> "$GITHUB_OUTPUT"
Tff7b72fi
Tff7b72fi

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffUpdateTa5d6ff Ta5d6ffdeviceTa5d6ff Ta5d6fflinksTa5d6ff Ta5d6fflist
Tff7b72idTb4b4b4: Ta5d6ffdevice_links
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72device_links_file_path="androidApp/src/main/assets/device_links.json"
Tff7b72temp_device_links_file="/tmp/new_device_links.json"

Tff7b72echo "Fetching latest device links..."
Tff7b72http_code=$(curl -s --max-time 90 -o "$temp_device_links_file" -w '%{http_code}' https://api.meshtastic.org/resource/deviceLinks || true)
Tff7b72http_code="${http_code:-0}"

Tff7b72if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
Tff7b72echo "::warning::Device links API returned HTTP $http_code. Skipping device links update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=HTTP $http_code from device links API" >> "$GITHUB_OUTPUT"
Tff7b72elif ! jq empty "$temp_device_links_file" 2>/dev/null; then
Tff7b72echo "::warning::Device links API returned invalid JSON data. Skipping device links update."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=Invalid JSON response from device links API" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72# This file is only the offline seed for the runtime cache, which refreshes
Tff7b72# itself from the same endpoint on a TTL. Guard against a degraded response
Tff7b72# (HTTP 200 but empty catalog) wiping the seed β€” mirrors the runtime, which
Tff7b72# ignores empty responses for the same reason.
Tff7b72link_count=$(jq -r 'if (.links | type) == "array" then (.links | length) else 0 end' "$temp_device_links_file" 2>/dev/null || echo 0)
Tff7b72if [ "$link_count" -eq 0 ]; then
Tff7b72echo "::warning::Device links API returned no links. Skipping to protect the bundled seed."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=empty links array from device links API" >> "$GITHUB_OUTPUT"
Tff7b72# Diff on `.links` only: the envelope carries a server-set `generatedAt`
Tff7b72# timestamp that changes on every response, so an hourly whole-document diff
Tff7b72# would open a churn PR even when the catalog is unchanged.
Tff7b72elif [ ! -f "$device_links_file_path" ] || ! jq --sort-keys '.links' "$temp_device_links_file" | diff -q - <(jq --sort-keys '.links' "$device_links_file_path"); then
Tff7b72echo "Changes detected in device links or local file missing. Updating $device_links_file_path."
Tff7b72# Pretty-print so the committed asset stays readable and future diffs stay
Tff7b72# minimal regardless of the API's response formatting.
Tff7b72jq . "$temp_device_links_file" > "$device_links_file_path"
Tff7b72echo "status=updated" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72echo "No changes detected in device links."
Tff7b72echo "status=unchanged" >> "$GITHUB_OUTPUT"
Tff7b72fi
Tff7b72fi

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffRefreshTa5d6ff Ta5d6ffObtainiumTa5d6ff Ta5d6ffconfigsTa5d6ff Ta5d6ffandTa5d6ff Ta5d6ffdeepTa5d6ff Ta5d6fflinks
Tff7b72idTb4b4b4: Ta5d6ffobtainium
Tff7b72envTb4b4b4:
T8b949e# Only for the API rate limit; the releases endpoint is public.
Tff7b72GITHUB_TOKENTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.tokenTa5d6ff Ta5d6ff}}
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72# Regenerates the deep-link tables and import files, then probes the live
Tff7b72# releases to confirm every apkFilterRegEx still matches published assets.
Tff7b72# A non-zero exit means an asset was renamed and the configs are broken β€”
Tff7b72# surface it as an error and skip the update rather than committing configs
Tff7b72# that resolve to nothing. Unreachable API exits 0 without touching files.
Tff7b72if python3 obtainium/generate-links.py --refresh; then
Tff7b72if git diff --quiet -- README.md docs/en/developer/test-builds.md obtainium; then
Tff7b72echo "No changes detected in Obtainium configs."
Tff7b72echo "status=unchanged" >> "$GITHUB_OUTPUT"
Tff7b72else
Tff7b72echo "Obtainium configs or channel status changed."
Tff7b72echo "status=updated" >> "$GITHUB_OUTPUT"
Tff7b72fi
Tff7b72else
Tff7b72echo "::warning::Obtainium config refresh failed β€” see the error above."
Tff7b72echo "status=error" >> "$GITHUB_OUTPUT"
Tff7b72echo "detail=an apkFilterRegEx no longer matches the published release assets" >> "$GITHUB_OUTPUT"
Tff7b72git checkout -- README.md docs/en/developer/test-builds.md obtainium
Tff7b72fi

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffSyncTa5d6ff Ta5d6ffwithTa5d6ff Ta5d6ffCrowdin
Tff7b72usesTb4b4b4: Ta5d6ffcrowdin/github-action@v2
Tff7b72withTb4b4b4:
Tff7b72base_urlTb4b4b4: Ta5d6ff'Ta5d6ffhttps://meshtastic.crowdin.com/api/v2Ta5d6ff'
Tff7b72configTb4b4b4: Ta5d6ff'Ta5d6ffcrowdin.ymlTa5d6ff'
Tff7b72crowdin_branch_nameTb4b4b4: Ta5d6ff'Ta5d6ffmainTa5d6ff'
Tff7b72upload_sourcesTb4b4b4: Ta5d6fftrue
Tff7b72upload_sources_argsTb4b4b4: Ta5d6ff'Ta5d6ff--preserve-hierarchyTa5d6ff'
Tff7b72upload_translationsTb4b4b4: Ta5d6fffalse
Tff7b72download_translationsTb4b4b4: Ta5d6fftrue
Tff7b72download_translations_argsTb4b4b4: Ta5d6ff'Ta5d6ff--preserve-hierarchyTa5d6ff'
Tff7b72create_pull_requestTb4b4b4: Ta5d6fffalse
Tff7b72commit_messageTb4b4b4: Ta5d6ff'Ta5d6ffchore(l10n):Te6edf3 Ta5d6ffNewTe6edf3 Ta5d6ffCrowdinTe6edf3 Ta5d6ffTranslationsTe6edf3 Ta5d6fffromTe6edf3 Ta5d6ffscheduledTe6edf3 Ta5d6ffupdateTa5d6ff'
Tff7b72push_translationsTb4b4b4: Ta5d6fffalse
Tff7b72push_sourcesTb4b4b4: Ta5d6fffalse
Tff7b72localization_branch_nameTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffgithub.ref_nameTa5d6ff Ta5d6ff}}
Tff7b72envTb4b4b4:
Tff7b72GITHUB_TOKENTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsecrets.CROWDIN_GITHUB_TOKENTa5d6ff Ta5d6ff}}
Tff7b72CROWDIN_PROJECT_IDTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsecrets.CROWDIN_PROJECT_IDTa5d6ff Ta5d6ff}}
Tff7b72CROWDIN_PERSONAL_TOKENTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsecrets.CROWDIN_PERSONAL_TOKENTa5d6ff Ta5d6ff}}

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffFixTa5d6ff Ta5d6fffileTa5d6ff Ta5d6ffpermissions
Tff7b72runTb4b4b4: Ta5d6ffsudoTa5d6ff Ta5d6ffchownTa5d6ff Ta5d6ff-RTa5d6ff Ta5d6ff$USER:$USERTa5d6ff Ta5d6ff.

T8b949e# Early warning for overlength store-listing translations just pulled from
T8b949e# Crowdin. Non-blocking on purpose: a hard failure here would abort the job
T8b949e# and stop the PR from being created. The hard gate is the check-metadata
T8b949e# job in pull-request.yml, which runs against the PR this workflow opens.
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCheckTa5d6ff Ta5d6ffstoreTa5d6ff Ta5d6ffmetadataTa5d6ff Ta5d6fflengths
Tff7b72continue-on-errorTb4b4b4: Ta5d6fftrue
Tff7b72runTb4b4b4: Ta5d6ffpython3Ta5d6ff Ta5d6ffscripts/check-metadata-length.py

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffBuildTa5d6ff Ta5d6ffPRTa5d6ff Ta5d6ffbody
Tff7b72idTb4b4b4: Ta5d6ffpr_body
Tff7b72envTb4b4b4:
T8b949e# Hoisted into env: (not interpolated directly in the script) so no step output can inject shell β€” see
T8b949e# zizmor template-injection. Values are fixed strings today, but detail may carry API text in future.
Tff7b72FIRMWARE_STATUSTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.firmware.outputs.statusTa5d6ff Ta5d6ff}}
Tff7b72FIRMWARE_DETAILTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.firmware.outputs.detailTa5d6ff Ta5d6ff}}
Tff7b72HARDWARE_STATUSTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.hardware.outputs.statusTa5d6ff Ta5d6ff}}
Tff7b72HARDWARE_DETAILTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.hardware.outputs.detailTa5d6ff Ta5d6ff}}
Tff7b72EVENT_FIRMWARE_STATUSTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.event_firmware.outputs.statusTa5d6ff Ta5d6ff}}
Tff7b72EVENT_FIRMWARE_DETAILTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.event_firmware.outputs.detailTa5d6ff Ta5d6ff}}
Tff7b72DEVICE_LINKS_STATUSTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.device_links.outputs.statusTa5d6ff Ta5d6ff}}
Tff7b72DEVICE_LINKS_DETAILTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.device_links.outputs.detailTa5d6ff Ta5d6ff}}
Tff7b72OBTAINIUM_STATUSTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.obtainium.outputs.statusTa5d6ff Ta5d6ff}}
Tff7b72OBTAINIUM_DETAILTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.obtainium.outputs.detailTa5d6ff Ta5d6ff}}
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72firmware_status="$FIRMWARE_STATUS"
Tff7b72firmware_detail="$FIRMWARE_DETAIL"
Tff7b72hardware_status="$HARDWARE_STATUS"
Tff7b72hardware_detail="$HARDWARE_DETAIL"
Tff7b72event_firmware_status="$EVENT_FIRMWARE_STATUS"
Tff7b72event_firmware_detail="$EVENT_FIRMWARE_DETAIL"
Tff7b72device_links_status="$DEVICE_LINKS_STATUS"
Tff7b72device_links_detail="$DEVICE_LINKS_DETAIL"
Tff7b72obtainium_status="$OBTAINIUM_STATUS"
Tff7b72obtainium_detail="$OBTAINIUM_DETAIL"

Tff7b72body="This PR includes automated updates from the scheduled workflow:"
Tff7b72body+=$'\n'

Tff7b72# Firmware status
Tff7b72case "$firmware_status" in
Tff7b72updated) body+=$'\n'"- βœ… \`firmware_releases.json\` updated from the Meshtastic API." ;;
Tff7b72unchanged) body+=$'\n'"- βœ”οΈ \`firmware_releases.json\` checked β€” no changes detected." ;;
Tff7b72error) body+=$'\n'"- ⚠️ \`firmware_releases.json\` skipped β€” ${firmware_detail}." ;;
Tff7b72*) body+=$'\n'"- ❓ \`firmware_releases.json\` β€” unknown status." ;;
Tff7b72esac

Tff7b72# Hardware status
Tff7b72case "$hardware_status" in
Tff7b72updated) body+=$'\n'"- βœ… \`device_hardware.json\` updated from the Meshtastic API." ;;
Tff7b72unchanged) body+=$'\n'"- βœ”οΈ \`device_hardware.json\` checked β€” no changes detected." ;;
Tff7b72error) body+=$'\n'"- ⚠️ \`device_hardware.json\` skipped β€” ${hardware_detail}." ;;
Tff7b72*) body+=$'\n'"- ❓ \`device_hardware.json\` β€” unknown status." ;;
Tff7b72esac

Tff7b72# Event firmware status
Tff7b72case "$event_firmware_status" in
Tff7b72updated) body+=$'\n'"- βœ… \`event_firmware.json\` updated from the Meshtastic API." ;;
Tff7b72unchanged) body+=$'\n'"- βœ”οΈ \`event_firmware.json\` checked β€” no changes detected." ;;
Tff7b72error) body+=$'\n'"- ⚠️ \`event_firmware.json\` skipped β€” ${event_firmware_detail}." ;;
Tff7b72*) body+=$'\n'"- ❓ \`event_firmware.json\` β€” unknown status." ;;
Tff7b72esac

Tff7b72# Device links status
Tff7b72case "$device_links_status" in
Tff7b72updated) body+=$'\n'"- βœ… \`device_links.json\` updated from the Meshtastic API." ;;
Tff7b72unchanged) body+=$'\n'"- βœ”οΈ \`device_links.json\` checked β€” no changes detected." ;;
Tff7b72error) body+=$'\n'"- ⚠️ \`device_links.json\` skipped β€” ${device_links_detail}." ;;
Tff7b72*) body+=$'\n'"- ❓ \`device_links.json\` β€” unknown status." ;;
Tff7b72esac

Tff7b72# Obtainium configs / deep links
Tff7b72case "$obtainium_status" in
Tff7b72updated) body+=$'\n'"- βœ… Obtainium deep links and import files regenerated (channel status changed)." ;;
Tff7b72unchanged) body+=$'\n'"- βœ”οΈ Obtainium configs checked against the live releases β€” no changes detected." ;;
Tff7b72error) body+=$'\n'"- ⚠️ Obtainium configs skipped β€” ${obtainium_detail}." ;;
Tff7b72*) body+=$'\n'"- ❓ Obtainium configs β€” unknown status." ;;
Tff7b72esac

Tff7b72# Crowdin (always attempted)
Tff7b72body+=$'\n'"- Source strings were uploaded to Crowdin."
Tff7b72body+=$'\n'"- Latest translations were downloaded from Crowdin (if available)."
Tff7b72body+=$'\n'
Tff7b72body+=$'\n'"Please review the changes."

Tff7b72# Write multi-line body to output
Tff7b72{
Tff7b72echo "content<<PREOF"
Tff7b72echo "$body"
Tff7b72echo "PREOF"
Tff7b72} >> "$GITHUB_OUTPUT"

Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCreateTa5d6ff Ta5d6ffPullTa5d6ff Ta5d6ffRequestTa5d6ff Ta5d6ffifTa5d6ff Ta5d6ffchangesTa5d6ff Ta5d6ffoccurred
Tff7b72usesTb4b4b4: Ta5d6ffpeter-evans/create-pull-request@v8
Tff7b72withTb4b4b4:
Tff7b72tokenTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsecrets.CROWDIN_GITHUB_TOKENTa5d6ff Ta5d6ff}}
Tff7b72commit-messageTb4b4b4: Tb4b4b4|
Tff7b72chore: Scheduled updates (Firmware, Hardware, Translations)

Tff7b72Automated updates for:
Tff7b72- Firmware releases list
Tff7b72- Device hardware list
Tff7b72- Event firmware metadata
Tff7b72- Device links list
Tff7b72- Crowdin source string uploads
Tff7b72- Crowdin translation downloads
Tff7b72- Obtainium deep links and import files
Tff7b72titleTb4b4b4: Ta5d6ff'Ta5d6ffchore:Te6edf3 Ta5d6ffScheduledTe6edf3 Ta5d6ffupdatesTe6edf3 Ta5d6ff(Firmware,Te6edf3 Ta5d6ffHardware,Te6edf3 Ta5d6ffTranslations)Ta5d6ff'
Tff7b72bodyTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffsteps.pr_body.outputs.contentTa5d6ff Ta5d6ff}}
Tff7b72branchTb4b4b4: Ta5d6ff'Ta5d6ffscheduled-updatesTa5d6ff'
Tff7b72baseTb4b4b4: Ta5d6ff'Ta5d6ffmainTa5d6ff'
Tff7b72delete-branchTb4b4b4: Ta5d6fftrue
Tff7b72add-pathsTb4b4b4: Tb4b4b4|
Tff7b72androidApp/src/main/assets/firmware_releases.json
Tff7b72androidApp/src/main/assets/device_hardware.json
Tff7b72androidApp/src/main/assets/event_firmware.json
Tff7b72androidApp/src/main/assets/device_links.json
Tff7b72fastlane/metadata/android/**
Tff7b72**/strings.xml
Tff7b72docs/**/*.md
Tff7b72README.md
Tff7b72obtainium/**
Tff7b72labelsTb4b4b4: Tb4b4b4|
Tff7b72automation
Tff7b72l10n
Tff7b72firmware
Tff7b72hardware

Served by rngit 1.5.2 - Generated in 0.1s